// ?：匹配不捕获
// arguments[0] 匹配到的标签
// arguments[1] 匹配到的标签名字
// 字母a-zA-Z_ - . 数组小写字母 大写字母
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`; // 标签名
// ?:匹配不捕获   <aaa:aaa>
const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
// startTagOpen 可以匹配到开始标签 正则捕获到的内容是 (标签名)
const startTagOpen = new RegExp(`^<${qnameCapture}`); // 标签开头的正则 捕获的内容是标签名
// 闭合标签 </xxxxxxx>  
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`); // 匹配标签结尾的 </div>
// <div aa   =   "123"  bb=123  cc='123'
// 捕获到的是 属性名 和 属性值 arguments[1] || arguments[2] || arguments[2]
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; // 匹配属性的
// <div >   <br/>
const startTagClose = /^\s*(\/?)>/; // 匹配标签结束的 >
// 匹配动态变量的  +? 尽可能少匹配
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g



export function parseHTML(html) {


  let root = null; // ast语法树的树根
  let currentParent; // 标识当前父级是谁
  let stack = [];
  const ELEMENT_TYPE = 1;
  const TEXT_TYPE = 3;

  function createASTElement(tagName, attrs) {
    return {
      tag: tagName,
      attrs,
      type: ELEMENT_TYPE,
      parent: null,
      children: []
    }
  }

  function start(tagName, attrs) {
    // console.log("标签名称是:", tagName, "属性是：", attrs);
    let element = createASTElement(tagName, attrs);
    if (!root) {
      root = element;
    }
    currentParent = element; // 把当前元素标记成父ast树
    stack.push(element);
  }
  function end(tagName) {
    // console.log("标签名称是:", tagName);
    // 1. 第一步：将标签栈中的标签抛出
    // 2. 第二步：获取当前栈中的最后一个标签
    // 3. 第三步：如果存在最后一个标签， 就把最后一个标签设为当前设为抛出的标签的父标签
    //           同时在最后一个标签中添加抛出的标签
    let element = stack.pop();
    currentParent = stack[stack.length - 1];
    if (currentParent) {
      element.parent = currentParent;
      currentParent.children.push(element);
    }
  }

  function chars(text) {
    // console.log("文本是： ", text);
    text = text.replace(/\s/g, "");
    if (text) {
      currentParent.children.push(
        {
          type: TEXT_TYPE,
          text
        }
      )
    }
  }

  // 不停的解析html字符串
  while (html) {
    let textEnd = html.indexOf("<");
    if (textEnd == 0) {
      // 如果当前索引是0，  肯定是一个标签， 开始标签或结束标签
      // 开始标签
      let startTagMatch = parseStartTag(); // 通过这个方法获取到匹配的结果 tagName， attrs
      if (startTagMatch) {
        start(startTagMatch.tagName, startTagMatch.attrs);
      }
      // 结束标签
      let endTagMatch = html.match(endTag);
      if (endTagMatch) {
        advance(endTagMatch[0].length);
        end(endTagMatch[1]);
      }
    }
    // 如果不是0， 就是文本
    let text;
    if (textEnd > 0) {
      text = html.substring(0, textEnd);
      chars(text);
    }
    if (text) {
      advance(text.length); // 删除文本内容
    }
  }
  function advance(n) {
    html = html.substring(n);
  }
  function parseStartTag() {
    let start = html.match(startTagOpen);
    if (start) {
      const match = {
        tagName: start[1],
        attrs: []
      }
      advance(start[0].length); // 将标签删除
      let end, attr;
      while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {
        // 将属性进行解析
        advance(attr[0].length) // 将属性删除
        match.attrs.push(
          {
            name: attr[1],
            value: attr[3] || attr[4] || attr[5]
          }
        )
      }
      if (end) { // 去掉开始标签的 > 
        advance(end[0].length);
        return match;
      }
    }
  }
  return root;
}